import json
import base64
import time
from decimal import Decimal
from typing import Dict, Any, Optional
from urllib.parse import urlencode, quote_plus
import aiohttp
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import rsa, padding

from .payment_base import (
    BasePaymentService, PaymentProvider, PaymentRequest, PaymentResponse,
    PaymentNotification, OrderStatus, PaymentMethod
)

class AlipayService(BasePaymentService):
    """支付宝支付服务实现"""
    
    def __init__(self, config: Dict[str, Any]):
        super().__init__(config)
        self.app_id = config.get("app_id")
        self.private_key = config.get("private_key")  # 应用私钥
        self.alipay_public_key = config.get("alipay_public_key")  # 支付宝公钥
        self.notify_url = config.get("notify_url", "")
        self.return_url = config.get("return_url", "")
        
        # API配置
        self.gateway_url = config.get("gateway_url", "https://openapi.alipay.com/gateway.do")
        self.sign_type = "RSA2"
        self.version = "1.0"
        self.charset = "utf-8"
        self.format = "JSON"
        
        # 加载私钥
        if self.private_key:
            self._private_key = serialization.load_pem_private_key(
                self.private_key.encode(),
                password=None
            )
        
        # 加载支付宝公钥
        if self.alipay_public_key:
            self._alipay_public_key = serialization.load_pem_public_key(
                self.alipay_public_key.encode()
            )
    
    def get_provider_name(self) -> PaymentProvider:
        return PaymentProvider.ALIPAY
    
    def get_supported_methods(self) -> list[PaymentMethod]:
        return [PaymentMethod.APP, PaymentMethod.H5, PaymentMethod.NATIVE]
    
    async def create_payment(self, request: PaymentRequest) -> PaymentResponse:
        """创建支付宝支付订单"""
        try:
            if request.payment_method == PaymentMethod.H5:
                return await self._create_h5_payment(request)
            elif request.payment_method == PaymentMethod.APP:
                return await self._create_app_payment(request)
            elif request.payment_method == PaymentMethod.NATIVE:
                return await self._create_native_payment(request)
            else:
                return PaymentResponse(
                    success=False,
                    error_message=f"不支持的支付方式: {request.payment_method}"
                )
        
        except Exception as e:
            return PaymentResponse(
                success=False,
                error_message=f"支付宝支付创建失败: {str(e)}"
            )
    
    async def _create_h5_payment(self, request: PaymentRequest) -> PaymentResponse:
        """创建H5支付"""
        method = "alipay.trade.wap.pay"
        
        biz_content = {
            "out_trade_no": request.order_id,
            "total_amount": str(request.amount),
            "subject": request.subject,
            "product_code": "QUICK_WAP_WAY",
        }
        
        if request.description:
            biz_content["body"] = request.description
        
        # 构建请求参数
        params = {
            "app_id": self.app_id,
            "method": method,
            "charset": self.charset,
            "sign_type": self.sign_type,
            "timestamp": self._get_timestamp(),
            "version": self.version,
            "return_url": request.return_url or self.return_url,
            "notify_url": request.notify_url or self.notify_url,
            "biz_content": json.dumps(biz_content, separators=(',', ':'))
        }
        
        # 生成签名
        sign = self._generate_sign(params)
        params["sign"] = sign
        
        # 构建支付URL
        payment_url = f"{self.gateway_url}?{urlencode(params)}"
        
        return PaymentResponse(
            success=True,
            order_id=request.order_id,
            payment_url=payment_url
        )
    
    async def _create_app_payment(self, request: PaymentRequest) -> PaymentResponse:
        """创建APP支付"""
        method = "alipay.trade.app.pay"
        
        biz_content = {
            "out_trade_no": request.order_id,
            "total_amount": str(request.amount),
            "subject": request.subject,
            "product_code": "QUICK_MSECURITY_PAY",
        }
        
        if request.description:
            biz_content["body"] = request.description
        
        # 构建请求参数
        params = {
            "app_id": self.app_id,
            "method": method,
            "charset": self.charset,
            "sign_type": self.sign_type,
            "timestamp": self._get_timestamp(),
            "version": self.version,
            "notify_url": request.notify_url or self.notify_url,
            "biz_content": json.dumps(biz_content, separators=(',', ':'))
        }
        
        # 生成签名
        sign = self._generate_sign(params)
        params["sign"] = sign
        
        # APP支付返回orderString供客户端调用
        order_string = urlencode(params)
        
        return PaymentResponse(
            success=True,
            order_id=request.order_id,
            payment_data={"order_string": order_string}
        )
    
    async def _create_native_payment(self, request: PaymentRequest) -> PaymentResponse:
        """创建扫码支付"""
        method = "alipay.trade.precreate"
        
        biz_content = {
            "out_trade_no": request.order_id,
            "total_amount": str(request.amount),
            "subject": request.subject,
        }
        
        if request.description:
            biz_content["body"] = request.description
        
        # 构建请求参数
        params = {
            "app_id": self.app_id,
            "method": method,
            "charset": self.charset,
            "sign_type": self.sign_type,
            "timestamp": self._get_timestamp(),
            "version": self.version,
            "notify_url": request.notify_url or self.notify_url,
            "biz_content": json.dumps(biz_content, separators=(',', ':'))
        }
        
        # 生成签名
        sign = self._generate_sign(params)
        params["sign"] = sign
        
        # 调用支付宝API
        async with aiohttp.ClientSession() as session:
            async with session.post(self.gateway_url, data=params) as response:
                result = await response.json()
                
                response_key = f"{method.replace('.', '_')}_response"
                if response_key in result:
                    resp_data = result[response_key]
                    if resp_data.get("code") == "10000":
                        return PaymentResponse(
                            success=True,
                            order_id=request.order_id,
                            qr_code=resp_data.get("qr_code")
                        )
                    else:
                        return PaymentResponse(
                            success=False,
                            error_message=resp_data.get("msg", "创建扫码支付失败")
                        )
                
                return PaymentResponse(
                    success=False,
                    error_message="支付宝API调用失败"
                )
    
    async def query_payment(self, order_id: str) -> Optional[PaymentNotification]:
        """查询支付宝支付状态"""
        try:
            method = "alipay.trade.query"
            
            biz_content = {
                "out_trade_no": order_id,
            }
            
            params = {
                "app_id": self.app_id,
                "method": method,
                "charset": self.charset,
                "sign_type": self.sign_type,
                "timestamp": self._get_timestamp(),
                "version": self.version,
                "biz_content": json.dumps(biz_content, separators=(',', ':'))
            }
            
            # 生成签名
            sign = self._generate_sign(params)
            params["sign"] = sign
            
            async with aiohttp.ClientSession() as session:
                async with session.post(self.gateway_url, data=params) as response:
                    result = await response.json()
                    
                    response_key = f"{method.replace('.', '_')}_response"
                    if response_key in result:
                        resp_data = result[response_key]
                        if resp_data.get("code") == "10000":
                            trade_status = resp_data.get("trade_status")
                            
                            if trade_status == "TRADE_SUCCESS":
                                status = OrderStatus.PAID
                            elif trade_status == "TRADE_CLOSED":
                                status = OrderStatus.CANCELLED
                            elif trade_status == "TRADE_FINISHED":
                                status = OrderStatus.PAID
                            else:
                                status = OrderStatus.PENDING
                            
                            return PaymentNotification(
                                order_id=order_id,
                                provider_order_id=resp_data.get("trade_no", ""),
                                status=status,
                                amount=Decimal(resp_data.get("total_amount", "0")),
                                currency="CNY",
                                transaction_id=resp_data.get("trade_no"),
                                raw_data=resp_data
                            )
            
            return None
        
        except Exception as e:
            print(f"查询支付宝支付状态失败: {e}")
            return None
    
    async def handle_notification(self, raw_data: Dict[str, Any]) -> Optional[PaymentNotification]:
        """处理支付宝支付通知"""
        try:
            # 验证签名
            sign = raw_data.pop("sign", "")
            sign_type = raw_data.pop("sign_type", "")
            
            if not self.verify_signature(raw_data, sign):
                print("支付宝通知签名验证失败")
                return None
            
            trade_status = raw_data.get("trade_status")
            
            if trade_status in ["TRADE_SUCCESS", "TRADE_FINISHED"]:
                return PaymentNotification(
                    order_id=raw_data.get("out_trade_no"),
                    provider_order_id=raw_data.get("trade_no"),
                    status=OrderStatus.PAID,
                    amount=Decimal(raw_data.get("total_amount", "0")),
                    currency="CNY",
                    transaction_id=raw_data.get("trade_no"),
                    raw_data=raw_data
                )
            
            return None
        
        except Exception as e:
            print(f"处理支付宝通知失败: {e}")
            return None
    
    async def refund_payment(self, order_id: str, amount: Decimal, reason: str = None) -> bool:
        """支付宝退款"""
        try:
            method = "alipay.trade.refund"
            
            biz_content = {
                "out_trade_no": order_id,
                "refund_amount": str(amount),
                "refund_reason": reason or "用户申请退款",
            }
            
            params = {
                "app_id": self.app_id,
                "method": method,
                "charset": self.charset,
                "sign_type": self.sign_type,
                "timestamp": self._get_timestamp(),
                "version": self.version,
                "biz_content": json.dumps(biz_content, separators=(',', ':'))
            }
            
            # 生成签名
            sign = self._generate_sign(params)
            params["sign"] = sign
            
            async with aiohttp.ClientSession() as session:
                async with session.post(self.gateway_url, data=params) as response:
                    result = await response.json()
                    
                    response_key = f"{method.replace('.', '_')}_response"
                    if response_key in result:
                        resp_data = result[response_key]
                        return resp_data.get("code") == "10000"
            
            return False
        
        except Exception as e:
            print(f"支付宝退款失败: {e}")
            return False
    
    def verify_signature(self, data: Dict[str, Any], signature: str) -> bool:
        """验证支付宝签名"""
        try:
            # 构建待验证字符串
            sorted_items = sorted(data.items())
            unsigned_string = "&".join([f"{k}={v}" for k, v in sorted_items if v])
            
            # 解码签名
            signature_bytes = base64.b64decode(signature)
            
            # 验证签名
            self._alipay_public_key.verify(
                signature_bytes,
                unsigned_string.encode('utf-8'),
                padding.PKCS1v15(),
                hashes.SHA256()
            )
            return True
        
        except Exception as e:
            print(f"验证支付宝签名失败: {e}")
            return False
    
    def _generate_sign(self, params: Dict[str, Any]) -> str:
        """生成支付宝签名"""
        # 过滤空值并排序
        filtered_params = {k: v for k, v in params.items() if v != ""}
        sorted_items = sorted(filtered_params.items())
        
        # 构建待签名字符串
        unsigned_string = "&".join([f"{k}={v}" for k, v in sorted_items])
        
        # 使用私钥签名
        signature = self._private_key.sign(
            unsigned_string.encode('utf-8'),
            padding.PKCS1v15(),
            hashes.SHA256()
        )
        
        # Base64编码
        return base64.b64encode(signature).decode('utf-8')
    
    def _get_timestamp(self) -> str:
        """获取时间戳"""
        return time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())